package com.bims.jreport.exception.handler;

import cn.hutool.core.util.StrUtil;
import com.bims.jreport.controller.RestResult;
import com.bims.jreport.utils.I18nMessagesUtil;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.web.ErrorProperties;
import org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController;
import org.springframework.boot.autoconfigure.web.servlet.error.ErrorViewResolver;
import org.springframework.boot.web.error.ErrorAttributeOptions;
import org.springframework.boot.web.servlet.error.ErrorAttributes;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 异常处理器，主要处理非Controller层抛出异常
 */

@Slf4j
public class ReportErrorController extends BasicErrorController implements ApplicationContextAware {

    @Autowired
    I18nMessagesUtil messageUtil;

    private static final String EXPCEPTION_ERROR_ATTR = "exception";

    private Map<String, ResponseStatus> exceptionResponseStatusMap = new ConcurrentHashMap<>();

    public ReportErrorController(ErrorAttributes errorAttributes,
                                 ErrorProperties errorProperties) {
        super(errorAttributes, errorProperties);
    }

    public ReportErrorController(ErrorAttributes errorAttributes,
                                 ErrorProperties errorProperties,
                                 List<ErrorViewResolver> errorViewResolvers) {
        super(errorAttributes, errorProperties, errorViewResolvers);
    }

    /**
     * 拦截器抛出异常信息处理(JSON)
     *
     * @param request
     * @return
     */
    @Override
    @RequestMapping(produces = {MediaType.APPLICATION_JSON_VALUE})
    public ResponseEntity error(HttpServletRequest request) {
        ErrorAttributeOptions options = isIncludeStackTrace(request, MediaType.ALL) ? ErrorAttributeOptions.of(ErrorAttributeOptions.Include.STACK_TRACE) : ErrorAttributeOptions.defaults();
        Map<String, Object> body = getErrorAttributes(request, options);
        String exception = (String) body.get(EXPCEPTION_ERROR_ATTR);
        HttpStatus httpStatus;
        String errorCode;
        if (exception != null && exceptionResponseStatusMap.containsKey(exception)) {
            val respStatus = exceptionResponseStatusMap.get(exception);
            httpStatus = respStatus.code();
            errorCode = respStatus.reason();
        } else {
            httpStatus = getStatus(request);
            errorCode = (String) body.get("errorCode");
        }
        RestResult restResponse;
        if (StrUtil.isNotBlank(errorCode)) {
            restResponse = RestResult.newFailedInstance(errorCode, messageUtil.getMessage(errorCode));
        } else {
            restResponse = RestResult.newFailedInstance((String) body.get("message"));
        }
        return new ResponseEntity<>(restResponse, httpStatus);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        val beansWithRespStatusAnnotation = applicationContext.getBeansWithAnnotation(ResponseStatus.class);
        beansWithRespStatusAnnotation.forEach((k, v) -> {
            val respStatusAnnotation = v.getClass().getAnnotation(ResponseStatus.class);
            exceptionResponseStatusMap.put(applicationContext.getBean(k).getClass().getName(), respStatusAnnotation);
        });
    }
}
